/**
  ******************************************************************************
  * @file   ili9341.c
  * @author Sifli software development team
  * @brief   This file includes the LCD driver for ILI9341 LCD.
  * @attention
  ******************************************************************************
*/
/**
 * @attention
 * Copyright (c) 2019 - 2022,  Sifli Technology
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Sifli integrated circuit
 *    in a product or a software update for such product, must reproduce the above
 *    copyright notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Sifli nor the names of its contributors may be used to endorse
 *    or promote products derived from this software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Sifli integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY SIFLI TECHNOLOGY "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL SIFLI TECHNOLOGY OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <rtthread.h>
#include "string.h"
#include "board.h"
//#include "drv_io.h"
//#include "drv_lcd.h"
#include "ili9341.h"

/** @addtogroup BSP
  * @{
  */

/** @addtogroup Components
  * @{
  */

/** @addtogroup ILI9341
  * @brief This file provides a set of functions needed to drive the
  *        ILI9341 LCD.
  * @{
  */

/** @defgroup ILI9341_Private_TypesDefinitions
  * @{
  */

typedef struct
{
    rt_uint16_t width;
    rt_uint16_t height;
    rt_uint16_t id;
    rt_uint8_t  dir;            //Horizontal or vertical screen control: 0, vertical; 1, horizontal
    rt_uint16_t wramcmd;
    rt_uint16_t setxcmd;
    rt_uint16_t setycmd;
} lcd_info_t;


/**
  * @}
  */

/** @defgroup ILI9341_Private_Defines
  * @{
  */
/**
  * @}
  */

/** @defgroup ILI9341_Private_Macros
  * @{
  */
#define RGB_ARRAY_LEN (320)

//Definition of scan direction
#define L2R_U2D  0
#define L2R_D2U  1
#define R2L_U2D  2
#define R2L_D2U  3
#define U2D_L2R  4
#define U2D_R2L  5
#define D2U_L2R  6
#define D2U_R2L  7
#define DFT_SCAN_DIR  L2R_U2D


//#define DEBUG

#ifdef DEBUG
    #define DEBUG_PRINTF(...)   rt_kprintf(__VA_ARGS__)
#else
    #define DEBUG_PRINTF(...)
#endif

static lcd_info_t lcddev;

/**
  * @}
  */

/** @defgroup ILI9341_Private_Variables
  * @{
  */

const LCD_DrvOpsDef ili9341_drv =
{
    ili9341_Init,
    ili9341_ReadID,
    ili9341_DisplayOn,
    ili9341_DisplayOff,
    ili9341_SetCursor,
    ili9341_SetRegion,
    ili9341_WritePixel,
    ili9341_WriteMultiplePixels,
    0,
    0,
    ili9341_SetDisplayWindow,
    ili9341_DrawHLine,
    ili9341_DrawVLine,
    ili9341_GetLcdPixelWidth,
    ili9341_GetLcdPixelHeight,
    ili9341_DrawBitmap,
    0,
};


static uint16_t ArrayRGB[RGB_ARRAY_LEN] = {0};


static const LCDC_InitTypeDef lcdc_int_cfg =
{
    .lcd_itf = LCDC_INTF_DBI_8BIT_A,
    .color_mode = LCDC_PIXEL_FORMAT_RGB565,
};

// LCD_DRIVER_EXPORT(ili9341, 0x9341, &lcdc_int_cfg, &ili9341_drv);

/**
  * @}
  */

/** @defgroup ILI9341_Private_FunctionPrototypes
  * @{
  */

/**
  * @}
  */

/** @defgroup ILI9341_Private_Functions
  * @{
  */

/**
  * @brief  Power on the LCD.
  * @param  None
  * @retval None
  */
void ili9341_Init(void)
{
    /* Initialize ILI9341 low level bus layer ----------------------------------*/
    LCD_IO_Init(&lcdc_int_cfg);

    /* Configure LCD */
#if 0
    ili9341_WriteReg(0xCA);
    ili9341_WriteData(0xC3);
    ili9341_WriteData(0x08);
    ili9341_WriteData(0x50);
#endif

    //according to the spec, 5ms delay is needed after LCD reset
    HAL_Delay(5);
    ili9341_WriteReg(LCD_POWERB);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0xC1);
    ili9341_WriteData(0x30);
    ili9341_WriteReg(LCD_POWER_SEQ);
    ili9341_WriteData(0x64);
    ili9341_WriteData(0x03);
    ili9341_WriteData(0x12);
    ili9341_WriteData(0x81);
    ili9341_WriteReg(LCD_DTCA);
    ili9341_WriteData(0x85);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x78);
    ili9341_WriteReg(LCD_POWERA);
    ili9341_WriteData(0x39);
    ili9341_WriteData(0x2C);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x34);
    ili9341_WriteData(0x02);
    ili9341_WriteReg(LCD_PRC);
    ili9341_WriteData(0x20);
    ili9341_WriteReg(LCD_DTCB);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x00);
#if 0
    ili9341_WriteReg(LCD_FRMCTR1);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x1B);
    //ili9341_WriteReg(LCD_DFC);
    //ili9341_WriteData(0x0A);
    //ili9341_WriteData(0xA2);
    ili9341_WriteReg(LCD_POWER1);
    ili9341_WriteData(0x10);
    ili9341_WriteReg(LCD_POWER2);
    ili9341_WriteData(0x10);
    ili9341_WriteReg(LCD_VCOM1);
    ili9341_WriteData(0x45);
    ili9341_WriteData(0x15);
    ili9341_WriteReg(LCD_VCOM2);
    ili9341_WriteData(0x90);
#endif
    ili9341_WriteReg(LCD_PIXEL_FORMAT);
    ili9341_WriteData(0x55);
    ili9341_WriteReg(LCD_MAC);
    ili9341_WriteData(0xE8);
    ili9341_WriteReg(LCD_3GAMMA_EN);
    ili9341_WriteData(0x00);

    //ili9341_WriteReg(LCD_RGB_INTERFACE);
    //ili9341_WriteData(0xC2);
    //ili9341_WriteReg(LCD_DFC);
    //ili9341_WriteData(0x0A);
    //ili9341_WriteData(0xA7);
    //ili9341_WriteData(0x27);
    //ili9341_WriteData(0x04);

#if 0
    /* Colomn address set */
    ili9341_WriteReg(LCD_COLUMN_ADDR);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0xEF);
    /* Page address set */
    ili9341_WriteReg(LCD_PAGE_ADDR);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x01);
    ili9341_WriteData(0x3F);
    //ili9341_WriteReg(LCD_INTERFACE);
    //ili9341_WriteData(0x01);
    //ili9341_WriteData(0x00);
    //ili9341_WriteData(0x06);
#endif

    ili9341_WriteReg(LCD_GRAM);
    HAL_Delay(1);

    ili9341_WriteReg(LCD_GAMMA);
    ili9341_WriteData(0x01);

    ili9341_WriteReg(LCD_PGAMMA);
    ili9341_WriteData(0x0F);
    ili9341_WriteData(0x29);
    ili9341_WriteData(0x24);
    ili9341_WriteData(0x0C);
    ili9341_WriteData(0x0E);
    ili9341_WriteData(0x09);
    ili9341_WriteData(0x4E);
    ili9341_WriteData(0x78);
    ili9341_WriteData(0x3C);
    ili9341_WriteData(0x09);
    ili9341_WriteData(0x13);
    ili9341_WriteData(0x05);
    ili9341_WriteData(0x17);
    ili9341_WriteData(0x11);
    ili9341_WriteData(0x00);
    ili9341_WriteReg(LCD_NGAMMA);
    ili9341_WriteData(0x00);
    ili9341_WriteData(0x16);
    ili9341_WriteData(0x1B);
    ili9341_WriteData(0x04);
    ili9341_WriteData(0x11);
    ili9341_WriteData(0x07);
    ili9341_WriteData(0x31);
    ili9341_WriteData(0x33);
    ili9341_WriteData(0x42);
    ili9341_WriteData(0x05);
    ili9341_WriteData(0x0C);
    ili9341_WriteData(0x0A);
    ili9341_WriteData(0x28);
    ili9341_WriteData(0x2F);
    ili9341_WriteData(0x0F);

    ili9341_WriteReg(LCD_SLEEP_OUT);
    HAL_Delay(1);
    ili9341_WriteReg(LCD_DISPLAY_ON);
}

/**
  * @brief  Disables the Display.
  * @param  None
  * @retval LCD Register Value.
  */
uint32_t ili9341_ReadID(void)
{
    //LCD_IO_Init();
    //return ((uint16_t)ili9341_ReadData(LCD_READ_ID4, LCD_READ_ID4_SIZE));
    return 0;
}

/**
  * @brief  Enables the Display.
  * @param  None
  * @retval None
  */
void ili9341_DisplayOn(void)
{
    /* Display On */
    ili9341_WriteReg(LCD_DISPLAY_ON);
}

/**
  * @brief  Disables the Display.
  * @param  None
  * @retval None
  */
void ili9341_DisplayOff(void)
{
    /* Display Off */
    ili9341_WriteReg(LCD_DISPLAY_OFF);
}

/**
  * @brief  Sets Cursor position.
  * @param  Xpos: specifies the X position.
  * @param  Ypos: specifies the Y position.
  * @retval None
  */
void ili9341_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
    uint8_t data = 0;
    LCD_IO_WriteReg(LCD_COLUMN_ADDR);
    data = (Xpos) >> 8;
    ili9341_WriteData(data);
    data = (Xpos) & 0xFF;
    ili9341_WriteData(data);
    data = (Xpos) >> 8;
    ili9341_WriteData(data);
    data = (Xpos) & 0xFF;
    ili9341_WriteData(data);
    LCD_IO_WriteReg(LCD_PAGE_ADDR);
    data = (Ypos) >> 8;
    ili9341_WriteData(data);
    data = (Ypos) & 0xFF;
    ili9341_WriteData(data);
    data = (Ypos) >> 8;
    ili9341_WriteData(data);
    data = (Ypos) & 0xFF;
    ili9341_WriteData(data);
    LCD_IO_WriteReg(LCD_GRAM);
}

void ili9341_SetRegion(uint16_t Xpos0, uint16_t Ypos0, uint16_t Xpos1, uint16_t Ypos1)
{
    uint8_t data = 0;
    LCD_IO_WriteReg(LCD_COLUMN_ADDR);
    data = (Xpos0) >> 8;
    ili9341_WriteData(data);
    data = (Xpos0) & 0xFF;
    ili9341_WriteData(data);
    data = (Xpos1) >> 8;
    ili9341_WriteData(data);
    data = (Xpos1) & 0xFF;
    ili9341_WriteData(data);
    LCD_IO_WriteReg(LCD_PAGE_ADDR);
    data = (Ypos0) >> 8;
    ili9341_WriteData(data);
    data = (Ypos0) & 0xFF;
    ili9341_WriteData(data);
    data = (Ypos1) >> 8;
    ili9341_WriteData(data);
    data = (Ypos1) & 0xFF;
    ili9341_WriteData(data);
    LCD_IO_WriteReg(LCD_GRAM);

    HAL_LCDC_SetROIArea(lcdc, Xpos0, Ypos0, Xpos1, Ypos1);
}

/**
  * @brief  Writes pixel.
  * @param  Xpos: specifies the X position.
  * @param  Ypos: specifies the Y position.
  * @param  RGBCode: the RGB pixel color
  * @retval None
  */
void ili9341_WritePixel(uint16_t Xpos, uint16_t Ypos, uint16_t RGBCode)
{
    uint8_t data = 0;
    if ((Xpos >= ILI9341_LCD_PIXEL_WIDTH) || (Ypos >= ILI9341_LCD_PIXEL_HEIGHT))
    {
        return;
    }

    /* Set Cursor */
    ili9341_SetCursor(Xpos, Ypos);

    LCD_IO_WriteData16(RGBCode);
}

void ili9341_WriteMultiplePixels(uint16_t *RGBCode, uint16_t Xpos0, uint16_t Ypos0, uint16_t Xpos1, uint16_t Ypos1)
{
    uint32_t size;
    if ((Xpos0 >= ILI9341_LCD_PIXEL_WIDTH) || (Ypos0 >= ILI9341_LCD_PIXEL_HEIGHT)
            || (Xpos1 >= ILI9341_LCD_PIXEL_WIDTH) || (Ypos1 >= ILI9341_LCD_PIXEL_HEIGHT))
    {
        return;
    }

    if ((Xpos0 > Xpos1) || (Ypos0 > Ypos1))
    {
        return;
    }

    /* Set Cursor */
    ili9341_SetRegion(Xpos0, Ypos0, Xpos1, Ypos1);

    HAL_LCDC_SendRectData_IT(hlcdc, (uint8_t *)RGBCode, Xpos0, Ypos0, Xpos1, Ypos1);
}

void ili9341_SetDisplayWindow(uint16_t Xpos, uint16_t Ypos, uint16_t Width, uint16_t Height)
{

    uint8_t data = 0;
    /* Column addr set, 4 args, no delay: XSTART = Xpos, XEND = (Xpos + Width - 1) */
    LCD_IO_WriteReg(LCD_COLUMN_ADDR);
    data = (Xpos) >> 8;
    ili9341_WriteData(data);
    data = (Xpos) & 0xFF;
    ili9341_WriteData(data);
    data = (Xpos + Width - 1) >> 8;
    ili9341_WriteData(data);
    data = (Xpos + Width - 1) & 0xFF;
    ili9341_WriteData(data);
    /* Row addr set, 4 args, no delay: YSTART = Ypos, YEND = (Ypos + Height - 1) */
    LCD_IO_WriteReg(LCD_PAGE_ADDR);
    data = (Ypos) >> 8;
    ili9341_WriteData(data);
    data = (Ypos) & 0xFF;
    ili9341_WriteData(data);
    data = (Ypos + Height - 1) >> 8;
    ili9341_WriteData(data);
    data = (Ypos + Height - 1) & 0xFF;
    ili9341_WriteData(data);
}


/**
  * @brief  Writes  to the selected LCD register.
  * @param  LCD_Reg: address of the selected register.
  * @retval None
  */
void ili9341_WriteReg(uint8_t LCD_Reg)
{
    LCD_IO_WriteReg(LCD_Reg);
}

/**
  * @brief  Writes data to the selected LCD register.
  * @param  LCD_Reg: address of the selected register.
  * @retval None
  */
void ili9341_WriteData(uint8_t RegValue)
{
    LCD_IO_WriteData8(RegValue);
}

/**
  * @brief  Draws horizontal line.
  * @param  RGBCode: Specifies the RGB color
  * @param  Xpos: specifies the X position.
  * @param  Ypos: specifies the Y position.
  * @param  Length: specifies the line length.
  * @retval None
  */
void ili9341_DrawHLine(uint16_t RGBCode, uint16_t Xpos, uint16_t Ypos, uint16_t Length)
{
    uint32_t counter = 0;

    if (Xpos + Length > ILI9341_LCD_PIXEL_WIDTH) return;

    /* Set Cursor */
    ili9341_SetRegion(Xpos, Ypos, Xpos + Length, Ypos);

    for (counter = 0; (counter < Length) && (counter < RGB_ARRAY_LEN); counter++)
    {
        ArrayRGB[counter] = RGBCode;
    }

    HAL_LCDC_SendRectData_IT(hlcdc, (uint8_t *)&ArrayRGB[0], Xpos, Ypos, Xpos + Length, Ypos);
}

/**
  * @brief  Draws vertical line.
  * @param  RGBCode: Specifies the RGB color
  * @param  Xpos: specifies the X position.
  * @param  Ypos: specifies the Y position.
  * @param  Length: specifies the line length.
  * @retval None
  */
void ili9341_DrawVLine(uint16_t RGBCode, uint16_t Xpos, uint16_t Ypos, uint16_t Length)
{
    uint32_t counter = 0;

    if (Ypos + Length > ILI9341_LCD_PIXEL_HEIGHT) return;
    for (counter = 0; (counter < Length) && (counter < RGB_ARRAY_LEN); counter++)
    {
        ili9341_WritePixel(Xpos, Ypos + counter, RGBCode);
    }
}




/**
  * @brief  Reads the selected LCD Register.
  * @param  RegValue: Address of the register to read
  * @param  ReadSize: Number of bytes to read
  * @retval LCD Register Value.
  */
uint32_t ili9341_ReadData(uint16_t RegValue, uint8_t ReadSize)
{
    /* Read a max of 4 bytes */
    //return (LCD_IO_ReadData(RegValue, ReadSize));
    return 0;
}

/**
  * @brief  Get LCD PIXEL WIDTH.
  * @param  None
  * @retval LCD PIXEL WIDTH.
  */
uint16_t ili9341_GetLcdPixelWidth(void)
{
    /* Return LCD PIXEL WIDTH */
    return ILI9341_LCD_PIXEL_WIDTH;
}

/**
  * @brief  Get LCD PIXEL HEIGHT.
  * @param  None
  * @retval LCD PIXEL HEIGHT.
  */
uint16_t ili9341_GetLcdPixelHeight(void)
{
    /* Return LCD PIXEL HEIGHT */
    return ILI9341_LCD_PIXEL_HEIGHT;
}

/**
  * @brief  Displays a bitmap picture loaded in the internal Flash.
  * @param  BmpAddress: Bmp picture address in the internal Flash.
  * @retval None
  */
void ili9341_DrawBitmap(uint16_t Xpos, uint16_t Ypos, uint8_t *pbmp)
{
    uint32_t index = 0, size = 0;

    /* Read bitmap size */
    size = *(volatile uint16_t *)(pbmp + 2);
    size |= (*(volatile uint16_t *)(pbmp + 4)) << 16;
    /* Get bitmap data address offset */
    index = *(volatile uint16_t *)(pbmp + 10);
    index |= (*(volatile uint16_t *)(pbmp + 12)) << 16;
    size = (size - index) / 2;
    pbmp += index;

    /* Set GRAM write direction and BGR = 0 */
    /* Memory access control: MY = 0, MX = 1, MV = 0, ML = 0 */
    ili9341_WriteReg(LCD_MAC);
    ili9341_WriteData(0x40);

    /* Set Cursor */
    ili9341_SetCursor(Xpos, Ypos);

    //TODO
    HAL_LCDC_SendRectData_IT(hlcdc, pbmp, Xpos, Ypos, Xpos + size, Ypos);

    /* Set GRAM write direction and BGR = 1 */
    /* Memory access control: MY = 1, MX = 1, MV = 0, ML = 0 */
    ili9341_WriteReg(LCD_MAC);
    ili9341_WriteData(0xC8);
}

static int ili9341_enable(int argc, char **argv)
{

}


MSH_CMD_EXPORT(spi_ili9341_enable, spi_ili9341_enable <ili9341_dev> <channel/-channel>);


/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT Sifli Technology *******END OF FILE****/
